import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
 * @author: congchi
 */
public class Main {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if(args.length <= 0){
			System.out.println("Error: Must supply a filename");
			return;
		}
		for(int i = 0; i < args.length; i++){
			String filename = args[i];
			System.out.println("\n\nRunning test sequence: " + filename);
			/*
			 * Executing the test sequence on set implementation 1
			 */
			printWMessage("MySet1 Implementation");
			MySet1 sut1 = new MySet1();
			ArrayList<Triple> testSeq1 = new ArrayList<Triple>();
			//Parse the input
			parseFile(filename,testSeq1);
			//execute the sequence
			execute(testSeq1, sut1);
			
			/*
			 * Executing the test sequence on set implementation 2
			 */
			printWMessage("MySet2 Implementation");
			MySet2 sut2 = new MySet2();
			execute(testSeq1, sut2);
			
			/*
			 * Executing the test sequence on set implementation 3
			 */
			printWMessage("MySet3 Implementation");
			MySet3 sut3 = new MySet3();
			execute(testSeq1, sut3);
			
			/*
			 * Executing the test sequence on HashSet
			 */
			printWMessage("HashSet Implementation");
			AbstractSet<String> sutHashSet = new HashSet<String>();
			execute(testSeq1, sutHashSet);
			
			/*
			 * Executing the test sequence on TreeSet
			 */
			printWMessage("TreeSet Implementation");
			AbstractSet<String> sutTreeSet = new TreeSet<String>();
			execute(testSeq1, sutTreeSet);
		}
	}
	
	private static void execute(ArrayList<Triple>testSeq, AbstractSet sut){
		for(Triple t : testSeq){
			String transitionName = t.transitionName;
			Boolean expectedOutput = t.expectedOutput;
			String newState = t.newState;
			System.out.println(t.toString());
			Boolean result = null;
			switch(TransitionName.valueOf(transitionName)){
				case addabc:
					result = sut.add("abc");
					if(expectedOutput){
						checkEqual(true, result);
					}else{
						checkEqual(false, result);
					}
					checkInState(newState,sut);
					break;
				case addxyz:
					result = sut.add("xyz");
					if(expectedOutput){
						checkEqual(true, result);
					}else{
						checkEqual(false, result);
					}
					checkInState(newState,sut);
					break;
				case removeabc:
					result = sut.remove("abc");
					if(expectedOutput){
						checkEqual(true, result);
					}else{
						checkEqual(false, result);
					}
					checkInState(newState,sut);
					break;
				case removexyz:
					result = sut.remove("xyz");
					if(expectedOutput){
						checkEqual(true, result);
					}else{
						checkEqual(false, result);
					}
					checkInState(newState,sut);
					break;
			}
		}
	}
	
	private static void checkInState(String state, AbstractSet sut){
		switch(State.valueOf(state)){
			case abc:
				if(sut.size() != 1){
					System.out.println("Error: The expected size of the set is 1. The current size is " + sut.size());
				}
				break;
				
			case abc_xyz:
				if(sut.size() != 2){
					System.out.println("Error: The expected size of the set is 2"
							+ "\nThe current size is " + sut.size());
				}
				break;
				
			case xyz:
				if(sut.size() != 1){
					System.out.println("Error: The expected size of the set is 1"
							+ "\nThe current size is " + sut.size());
				}
				break;
			
			case empty:
				if(sut.size() != 0){
					System.out.println("Error: The expected size of the set is 0"
							+ "\nThe current size is " + sut.size());
				}
				break;
		}
	}

	private static void checkEqual(Boolean expectedValue, Boolean actualValue){
		if(expectedValue != actualValue){
			System.out.println("Error. Expected: " + expectedValue + " Actual: " + actualValue);
		}
	}
	
	private static void parseFile(String filename, ArrayList<Triple>testSeq){
		//Read the file
		try{
			BufferedReader in = new BufferedReader(new FileReader(filename));
			while(true){
				String line = in.readLine();
				if(line == null){
					break;
				}
				Pattern p = Pattern.compile("[a-z]+, [a-z]+, [a-z_]+");
				Matcher m = p.matcher(line);
				if(m.find()){
					p = Pattern.compile(",");
					String[] b = p.split(line.substring(m.start(), m.end()));
					Triple tmpTriple = new Triple(b);
					testSeq.add(tmpTriple);
				}
			}	
		}catch(IOException e){
			System.out.println(e.getMessage());
		}
	}
	
	private static void printWMessage(String msg){
		System.out.println("*********************************" +
		           		   "*" +
		           		   "* " + msg
		           		   +"*"
		           		   +"********************************" 
		           );
	}
	
	public static enum TransitionName{
		addabc, addxyz, removeabc, removexyz
	}
	
	public static enum State{
		abc, abc_xyz, xyz, empty
	}
	
	public static class Triple{
		public String transitionName;
		public Boolean expectedOutput;
		public String newState;
		
		public Triple(String transitionName, String expectedResult, String newState){
			this.transitionName = transitionName;
			this.expectedOutput = Boolean.valueOf(expectedResult);
			this.newState = newState;
		}
		
		public Triple(String[] arr){
			if(arr == null){
				System.out.println("Triple; Error: Array is empty");
				return;
			}
			if(arr.length != 3 ){
				System.out.println("Triple; Error: The number of elements must be equal to three");
				return;
			}
			this.transitionName = arr[0].trim();
			this.expectedOutput = Boolean.valueOf(arr[1].trim());
			this.newState = arr[2].trim();
		}
		
		public String toString(){
			return "(" + transitionName +", " + expectedOutput + ", " + newState + ")";
		}
	}
}
